---------------------------------------- Chapitre VII - Hachage ----------------------------------------
Deux exemples de protection basée sur des hash MD5 - 1/2
Cette fois-ci, nous allons voir deux exemples de protections basées sur des hash MD5.
Outils nécessaires :
OllyDbg (by Oleh Yuschuk)
Keygener Assitant v1.7 (by Mr Paradox
& RobenHoodArab, http://www.at4re.com) pour les calculs, ou tout autre outil calculant des hashs MD5.
J'aime bien Keygenner Assistant pour son aspect "Tout en un" et également car il intègre un Sniffer de hash et de crypto.
PEiD et son plugin kanal
(by Jibz, Qwerton & snaker, http://www.peid.info/) ou tout autre analyseur d'exécutables !
- Les notions de base des précédents cours sur l’assembleur et l’utilisation d’Ollydbg sont considérées acquises.
RAPPEL
Tout le contenu de ce tutoriel est fourni à titre purement éducatif, et ludique. N'utilisez pas ce logiciel en dehors des autorisations fixées par la licence d'utilisation de celui-ci. Si vous ne disposez pas de licence valide pour ce logiciel, vous devez le désinstaller à l'échéance de sa période d'évaluation. Nous ne sommes en aucun cas responsable de l'usage que vous faites de ce programme et de ces tutoriels.
I - Premier cas : RegistryFast
Cible : RegistryFast v5.0
Editeur : RegistryFast.com
Je pense que l'on pourrait y attribuer un niveau de difficulté de 1 ou 1.5 sur une échelle de 5 (le serial correct n'étant pas comparé directement avec ce qui a été entré), l'exécutable n'est pas packé, ni obfusqué. De plus, aucun anti-debugger ni quoi que ce soit pour contrer l'analyse n'a été intégré...
Notre premier réflexe est bien sûr de scanner notre exécutable principal avec notre analyseur préféré. D'une manière générale, ProtectionID est celui que j'utilise le plus souvent pour la fiabilité de ses signatures et ses mises à jour encore régulières, mais, pour ce cas, je vais utiliser PEiD et le plugin Kanal, qui permet la détection de signatures cryptographiques :
Nous apprenons que notre cible est codée en Delphi, et surtout qu'elle ne semble pas être packée. Par ailleurs, Kanal nous indique l'utilisation d'un certain nombre de fonctions de crypto, que nous laisserons de côté pour le moment...
Ouvrons donc notre cible avec Olly, et tentons de nous enregistrer. Utilisez deux caractères numériques pour les 2 premières valeurs de votre serial... ou bien vous aurez droit à un beau: " "Votre lettre" is not a valid integrer value! " et vous n'obtiendrez pas le "bon" message d'erreur :
On va pouvoir commencer à travailler . Mettez Olly en Pause et
allons jeter un œil dans la pile des Calls, appelé "Call Stack" :
Le but est de trouver un point antérieur à l'arrivée de la MessageBox, le plus prêt possible de la routine.
On aurait également pu faire des exécutions jusqu’au RET, nous serions arrivés au même point, mais il faut
savoir s'économiser quelques clics . Vous allez double-cliquer
sur la ligne à l'adresse 0012FB6C, ce qui nous conduira en 004DCAA4. En toute logique, il faudrait également aller
voir en 0048A630... mais il n'y a rien d'intéressant là-bas
.
Donc une fois en 004DCAA4, faîtes un petit coup de scroll down, voir si on peut se faire une idée de ce qui se trame dans
cette routine...et Ô bonheur :
004DCC81 |. B9 50CD4D00 MOV ECX,RegFast.004DCD50 ; ASCII "Registry Fast"
004DCC86 |. BA 60CD4D00 MOV EDX,RegFast.004DCD60 ; ASCII "Invalid serial number! Please try again..."
[...]
004DCCA9 |. B9 50CD4D00 MOV ECX,RegFast.004DCD50 ; ASCII "Registry Fast"
004DCCAE |. BA 60CD4D00 MOV EDX,RegFast.004DCD60 ; ASCII "Invalid serial number! Please try again..."
Nul doute, nous devons être au bon endroit .
Posons donc un BP en 004DCAA4, et on recommence. Je saisis mon fake serial, pour moi, ce sera : " 59ABCDEFGHIJKL1111222223344445555" (notez que même si vous saisissez en minuscules, les caractères seront automatiquement mis en majuscules). N’oubliez pas de commencer par deux chiffres, puis on teste notre serial.
Comme prévu, nous breakons en 004DCAA4. Traçons un peu en step over, remarquez le CharLower :
004DCADF |. 8B45 EC MOV EAX,[LOCAL.5] ; serial dans EAX
004DCAE2 |. 8D55 FC LEA EDX,[LOCAL.1] ; pointeur vers le Buffer de reception
004DCAE5 |. E8 02C5F2FF CALL RegFast.00408FEC ; Char Lower
On se retrouve avec "59abcdefghijkl1111222223344445555".
La partie suivante du code retire les éventuels tirets du serial :
004DCAF6 |. BA 20CD4D00 MOV EDX,RegFast.004DCD20 ; => Suivez cette valeur dans le dump pour voir qu’elle vaut 0x2Dh
004DCAFB |. 8B45 FC MOV EAX,[LOCAL.1]
004DCAFE |. E8 8523F3FF CALL RegFast.0040EE88
Ensuite, on attaque les choses plus sérieuses. Les commentaires vous épargneront un long discours…
Regardons tout ça. D’une manière générale, les opérations se font de la manière suivante :
MOV EAX, [Adresse du Buffer de réception]
PUSH EAX Param 1 (nombre de caractères) Param 2 (Position dans la chaîne)
MOV EAX, [Chaîne à modifier]
CALL FONCTION
Pour visualiser le résultat d’un CALL, il vous suffit de suivre EAX dans le dump, sélectionner le DWORD qui s’y trouve, et faire un " Follow DWORD in dump".
Dans l’ordre, nous aurons les actions suivantes :
Petite astuce : Vous pouvez définir les labels également sur les variables. Si vous souhaitez nommer les variables Chaine1, Chaine2,
Chaine3, …, rendez vous à l’adresse en question dans le dump et appuyez sur la touche " : " pour personnaliser le label des
variables
A ce niveau nous savons donc que notre serial devra faire au moins 8+ 9= 17d caractères sans les tirets. Et que les 2 premiers caractères doivent être compris entre 0 et 9. Nous savons que nous avons à faire à une constante, car elle est visible dans les SDR avant même de lancer le soft. Elle n’est donc pas créée dynamiquement. Et c’est tout !
Continuons notre route…
004DCC06 |. 8D45 C4 LEA EAX,[LOCAL.15]
004DCC09 |. 50 PUSH EAX
004DCC0A |. FF75 F8 PUSH [LOCAL.2] ; 8 caractères de notre serial
qu'on a appelé Chaine1
004DCC0D |. FF75 F0 PUSH [LOCAL.4] ; les 10 Caractères de la constante
que l'on a appelé Chaine4
004DCC10 |. FF75 F4 PUSH [LOCAL.3] ; 3eme char...qu'on a appelé
Chaine2 ;)
004DCC13 |. 8D45 BC LEA EAX,[LOCAL.17] ; Placement de l'adresse du Buffer dans EAX
004DCC16 |. BA 03000000 MOV EDX,3 ; Nbre de chaines à
concaténer
004DCC1B |. E8 8882F2FF CALL RegFast.00404EA8 ; Concatenation
On poursuit les opérations avec une concaténation : Chaine7 = Chaine1Chaine4Chaine2
004DCC20 |. 8B45 BC MOV EAX,[LOCAL.17] ; Chaine7 dans EAX
004DCC23 |. 8D55 C0 LEA EDX,[LOCAL.16] ; Nouveau Buffer dans EDX
004DCC26 |. E8 F995FDFF CALL RegFast.004B6224 ; HASH MD5 de Chaine7
Chaine7 est donc aussitôt placée dans EAX. Puis un buffer vide est placé dans EDX. En regardant la valeur de sortie de fonction, on remarque tout de suite un hash type MD5 :
Stack SS:[0012F7A4]=016615E0, (ASCII "63fc3ba6906c959c546ca204277df1ec")
EAX=0012F774
D’une part, c’est exactement la même longueur qu’un hash MD5 (32d caractères), de plus, Kanal nous
avait sniffé, entre autres, du MD5. Et surtout, j’ai hashé Chaine7 en MD5 avec Keygenner Assistant, et j’ai eu le même
résultat . La question est close !
Je sais pas pourquoi, mais je sens qu’on touche au but !
004DCC2B |. 8B45 C0 MOV EAX,[LOCAL.16]
; Notre Hash dans EAX
004DCC2E |. B9 05000000 MOV ECX,5
; Nombre de
caractères a récupérer
004DCC33 |. 8BD6 MOV EDX,ESI
; décalage dans le Hash de
ESI qui vaut toujours Compteur1
004DCC35 |. E8 0E84F2FF CALL RegFast.00405048
004DCC3A |. 8B55 C4 MOV EDX,[LOCAL.15]
; On place dans EDX les 5 caractères récupérés
004DCC3D |. 8B45 FC MOV EAX,[LOCAL.1]
; On place notre Chaine6 dans EAX
004DCC40 |. E8 EF82F2FF CALL RegFast.00404F34 ; Celui
là, il ne serait pas bête d'y jeter un oeil ;)
004DCC45 |. 75 38 JNZ SHORT RegFast.004DCC7F ;
saute vers BadBoy si =! 0 :(
004DCC47 |. C683 30030000 MOV BYTE PTR DS:[EBX+330],1 ; Si on voulait cracker, on se pencherait sur cette variable ;)
004DCC4E |. 8D55 B4 LEA EDX,[LOCAL.19]
004DCC51 |. 8B83 F8020000 MOV EAX,DWORD PTR DS:[EBX+2F8]
004DCC57 |. E8 B0CDF8FF CALL RegFast.00469A0C
004DCC5C |. 8B45 B4 MOV EAX,[LOCAL.19]
004DCC5F |. 8D55 B8 LEA EDX,[LOCAL.18]
004DCC62 |. E8 99C5F2FF CALL RegFast.00409200
004DCC67 |. 8B45 B8 MOV EAX,[LOCAL.18]
004DCC6A |. E8 198E0400 CALL RegFast.00525A88
004DCC6F |. 8BC3 MOV EAX,EBX
004DCC71 |. E8 16010000 CALL RegFast.004DCD8C
004DCC76 |. 8BC3 MOV EAX,EBX
004DCC78 |. E8 53A0FAFF CALL RegFast.00486CD0
004DCC7D |. EB 4E JMP SHORT RegFast.004DCCCD
; Saute au dessus des Badboy ;) , Surement vers le GoodBoy :)
004DCC7F |> 6A 30 PUSH 30
004DCC81 |. B9 50CD4D00 MOV ECX,RegFast.004DCD50 ; ASCII "Registry Fast"
004DCC86 |. BA 60CD4D00 MOV EDX,RegFast.004DCD60 ; ASCII "Invalid serial number! Please try
again..."
On voit donc qu’on récupère 5 caractères dans notre hash à partir du ESIème caractère (qui vaut maintenant la valeur du 2ème caractère +1, notre Compteur1. En rentrant dans le CALL 00404F34, on voit bien que l’on compare la chaine extraite de notre hash avec Chaine6, soit les caractères 4 à 9 de notre serial entré. Récapitulons les dernières étapes :
On teste donc notre nouveau serial : "59a06c95ghijkl1111222223344445555" :
>Et voili, et voilou !
Nous en avons terminé avec celui-ci, vous avez tout ce qu’il vous faut pour faire un petit keygen.